
import { MarkerToMark } from "./marker_to_mark.js"
import { ArToolkitNFT } from "./ar_toolkit_nft.js"
import { ImageToNft } from "./image_to_nft.js"
/* nft 图像识别追踪 three 渲染 */
export class Know {
    /**
     * @param { Object }  param 参数
     */
    constructor(param) {
        /* 识别类型 image：图片识别 markerUrl传入png图片地址  nft ：nft识别 markerUrl传入nft文件地址 */
        this.koneType = param.koneType || "nft"
        /* 生成的识别图文件路径 （nft识别图生成地址：https://carnaux.github.io/NFT-Marker-Creator/#/） || png图片地址 */
        this.markerUrl = param.markerUrl || null;
        /* 摄像头 */
        this.cameraOrigin = param.cameraOrigin || null;
        /* 摄像头参数数据文件 */
        this.cameraPara = param.cameraPara || null;
        /* three threeRenderer webgl canvas */
        this.threeRenderer = param.threeRenderer || null
        /* three3d场景追踪是否启用 */
        this.three3d = param.three3d === undefined ? true : param.three3d
        /* 渲染模型根盒子 */
        this.rootModel = param.rootModel || null
        /* 渲染模型 */
        this.model = param.model || null
        /* 相机 */
        this.threeCamera = param.threeCamera || null
        /* 场景 */
        this.threeScene = param.threeScene || null
        /* 模型缩放 */
        this.threeModelScale = param.threeModelScale || 1
        /* 模型位置 */
        this.threeModelPosition = param.threeModelPosition || null
        /* 模型旋转 */
        this.threeRotation = param.threeRotation === undefined ? true : param.threeRotation
        /* 模型矩阵定位 */
        this.threeMatrix = param.threeMatrix === undefined ? true : param.threeMatrix
        /* 离屏canvas */
        this.canvasEl = document.createElement('canvas');
        this.canvasElCtx = this.canvasEl.getContext('2d')
        /* ARToolkitNFT */
        this.arController = null
        /* marker */
        this.marker = null
        /* 图像矩阵 */
        this.matrix = null
        this.matrixWidth = 0
        this.matrixHeight = 0
        /* 相机矩阵 */
        this.cameraMatrix = null
        /* 摄像头图像宽高 */
        this.cameraOriginWidth = 0
        this.cameraOriginHeight = 0
        /* 动画id */
        this.requestAnimationFrameId = null
        /* 识别图识别到的数据 */
        this.matrixGL_RH = null
        /* event 监听事件集合 */
        this.eventListener = {}
        this.markerToMark = new MarkerToMark()
    }
    /**
     * 初始化
     */
    async Init() {
        try {
            if (!this.markerUrl) {
                throw new Error(`请传入识别图${this.koneType}文件地址`)
            }
            if (!this.cameraOrigin) {
                throw new Error("请传入摄像头数据")
            }

            if (this.three3d && this.rootModel) {
                this.rootModel.visible = false;
            }

            this.widthAndHeightComputed()
            this.arController = await ArToolkitNFT.initWithDimensions(
                this.matrixWidth,
                this.matrixHeight,
                this.cameraPara
            )
            if (this.three3d) {
                if (!this.threeRenderer) {
                    throw new Error("请传入当前3d场景渲染器： THREE.Renderer")
                }
                if (!this.threeScene) {
                    throw new Error("请传入当前3d场景： THREE.Scene")
                }
                if (!this.threeCamera) {
                    throw new Error("请传入当前3d场景相机： THREE.Camera")
                }
                if (!this.model) {
                    throw new Error("请传入渲染模型")
                }
                if (!this.rootModel) {
                    throw new Error("请传入一个THREE.Object3D()的模型根盒子 且添加model模型")
                }
                /* 相机矩阵 */
                this.cameraMatrix = this.arController.getCameraMatrix();
                this.cameraMatrixInit()
            }
            /* 监听NFT识别*/
            this.arController.addEventListener("getNFTMarker", (ev) => {
                this.matrixGL_RH = ev.data.matrixGL_RH
            });
            /* 加载识别图点位 */
            this.arController.loadNFTMarker(this.koneType === "image" ? await this.imageToNft() : this.markerUrl, (id) => {
                this.arController.trackNFTMarkerId(id);
                this.marker = this.arController.getNFTData(this.arController.id, 0);
                if (this.three3d) {
                    this.model.position.y = ((this.marker.height / this.marker.dpi) * 2.54 * 10) / 2.0;
                    this.model.position.x = ((this.marker.width / this.marker.dpi) * 2.54 * 10) / 2.0;
                    this.model.scale.set(this.threeModelScale, this.threeModelScale, this.threeModelScale)
                    if (this.threeModelPosition) {
                        this.model.position.set(this.model.position.x + this.threeModelPosition.x || 0, this.model.position.y + this.threeModelPosition.y || 0, this.model.position.z + this.threeModelPosition.z || 0);
                    }
                }
                this.emit("loaded", this.marker)
                if (!this.three3d) {
                    this.loop()
                }

            }, err => {
                throw err
            });
        } catch (err) {
            return Promise.reject(err)
        }
    }
    async imageToNft() {
        let imageToNft = new ImageToNft()
        return await imageToNft.convert(this.markerUrl)
    }
    /**
     * 模型位置大小设置 
     */
    modelBoxInit(marker) {
        if (this.threeRotation) {
            this.rootModel.rotation.setFromVector3(marker[1]);
        }
        this.rootModel.position.set(marker[0].x, marker[0].y, marker[0].z);
        this.rootModel.scale.set(marker[2].x, marker[2].y, marker[2].z);
    }
    /**
     * 发送捕捉到的图片去识别 
     */
    process(imageData) {
        this.matrixGL_RH = null
        this.arController.process(imageData)
        /* 识别到图片 */
        if (this.matrixGL_RH) {
            if (this.three3d) {
                this.rootModel.visible = true;
                if (this.threeMatrix) {
                    /*  矩阵模式 */
                    this.rootModel.matrixAutoUpdate = false;
                    this.setMatrix(this.rootModel.matrix, this.interpolate(this.matrixGL_RH))
                } else {
                    this.rootModel.matrixAutoUpdate = true;
                    this.modelBoxInit(this.markerToMark.update(this.matrixGL_RH))
                }
            }

            this.emit("found", this.matrixGL_RH)
        } else {
            if (this.three3d) {
                this.rootModel.visible = false;
            }
            this.emit("notFound", this.matrixGL_RH)

        }
    }
    /**
     * 更新识别追踪
     * @param { Object } model 追踪的模型 
     */
    update() {
        this.draw()
    }
    /**
     * 截取摄像头流数据
     */
    draw() {
        this.canvasElCtx.fillStyle = 'black';
        this.canvasElCtx.fillRect(0, 0, this.matrixWidth, this.matrixHeight);
        this.canvasElCtx.drawImage(this.cameraOrigin.videoEl, 0, 0, this.cameraOrigin.width, this.cameraOrigin.height, (this.matrixWidth - this.cameraOriginWidth) / 2, (this.matrixHeight - this.cameraOriginHeight) / 2, this.cameraOriginWidth, this.cameraOriginHeight)
        let imageData = this.canvasElCtx.getImageData(0, 0, this.matrixWidth, this.matrixHeight)
        this.process(imageData)

    }
    /**
     * 识别 
     */
    loop() {
        this.draw()
        this.requestAnimationFrameId = requestAnimationFrame(this.loop.bind(this))
    }
    /**
     * 设置模型矩阵 
     */
    setMatrix(matrix, value) {
        let array = [];
        for (let key in value) {
            array[key] = value[key];
        }
        if (typeof matrix.elements.set === "function") {
            matrix.elements.set(array);
        } else {
            matrix.elements = [].slice.call(array);
        }
    };
    /**
     * 矩阵转换 
     */
    interpolate(matrix) {
        const interpolationFactor = 24;
        let delta = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
        let interpolated = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
        // interpolate matrix
        for (let i = 0; i < 16; i++) {
            delta[i] = matrix[i] - interpolated[i];
            interpolated[i] = interpolated[i] + delta[i] / interpolationFactor;
        }
        return interpolated
    }
    /**
     * 相机矩阵初始化 
     */
    cameraMatrixInit() {
        let ratioW = this.matrixWidth / this.cameraOriginWidth;
        let ratioH = this.matrixHeight / this.cameraOriginHeight;
        this.cameraMatrix[0] *= ratioW;
        this.cameraMatrix[4] *= ratioW;
        this.cameraMatrix[8] *= ratioW;
        this.cameraMatrix[12] *= ratioW;
        this.cameraMatrix[1] *= ratioH;
        this.cameraMatrix[5] *= ratioH;
        this.cameraMatrix[9] *= ratioH;
        this.cameraMatrix[13] *= ratioH;
        this.setMatrix(this.threeCamera.projectionMatrix, this.cameraMatrix);
    }
    /* 销毁 */
    dispose() {
        if (!this.three3d) {
            cancelAnimationFrame(this.requestAnimationFrameId)
        }
        this.arController.removeEventListener("getNFTMarker")
        this.arControlle = null
    }
    /**
     * 宽高计算 
     * 设置离屏canvas宽高
     */
    widthAndHeightComputed() {
        let pscale = 320 / Math.max(this.cameraOrigin.width, this.cameraOrigin.height / 3 * 4);
        this.cameraOriginWidth = this.cameraOrigin.width * pscale;
        this.cameraOriginHeight = this.cameraOrigin.height * pscale;
        this.matrixWidth = Math.max(this.cameraOriginWidth, this.cameraOriginHeight / 3 * 4);
        this.matrixHeight = Math.max(this.cameraOriginHeight, this.cameraOriginWidth / 4 * 3);
        this.canvasEl.style.clientWidth = this.matrixWidth + "px";
        this.canvasEl.style.clientHeight = this.matrixHeight + "px";
        this.canvasEl.width = this.matrixWidth;
        this.canvasEl.height = this.matrixHeight;
    }
    /**
     * 事件监听注册 
     */
    on(name, fn) {
        this.eventListener[name] = fn
    }
    /**
     * 事件触发 
     */
    emit(name, value) {
        this.eventListener[name] && this.eventListener[name](value)
    }
}
